package ph.mallody.lalamove.api;

import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import org.apache.http.HttpEntity;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import ph.mallody.lalamove.api.results.*;
import ph.mallody.lalamove.common.Environment;
import ph.mallody.lalamove.common.HttpClientCreater;
import ph.mallody.lalamove.common.LalamoveException;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.net.URI;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;

public class LalaMove {

    private static Gson gson = new Gson();
    /**
     * 点到点运送报价
     * @return
     */
    public static QuotationResult quotation(Address from, Address stop) {
        List<Address> stops = new ArrayList<>();
        stops.add(stop);
        return quotation(from, stops);
    }
    public static QuotationResult quotation(Address from, List<Address> stops) {
        return quotation(from, stops, null);
    }
    public static QuotationResult quotation(Address from, List<Address> stops, Date scheduleTime) {
        return executeQuotation(from, stops, scheduleTime);
    }
    /**
     * 下单
     */
    public static PlaceOrderResult placeOrder(Address from, Address stop, int amount) {
        List<Address> stops = new ArrayList<>();
        stops.add(stop);
        return placeOrder(from, stops ,amount);
    }
    public static PlaceOrderResult placeOrder(Address from, List<Address> stops, int amount) {
        return placeOrder(from, stops, null, amount);
    }
    public static PlaceOrderResult placeOrder(Address from, List<Address> stops, Date scheduleTime, int amount) {
        return executePlaceOrder(from, stops, scheduleTime, amount);
    }
    /**
     * 取订单信息
     */
    public static OrderDetailResult getOrderDetail(String customerOrderId) {
        CloseableHttpClient httpClient = HttpClientCreater.create();
        HttpGet get = new HttpGet();
        try {
            wrapGet(get, "/v2/orders/" + customerOrderId);
            CloseableHttpResponse response = httpClient.execute(get);
            //执行请求
            int statusCode = response.getStatusLine().getStatusCode();
            String json = "";
            if (statusCode == 200 || statusCode == 201) {
                json = EntityUtils.toString(response.getEntity());
            } else {
                String message = checkError(response);
                response.close();
                throw new LalamoveException(message);
            }
            response.close();
            OrderDetailResult result = gson.fromJson(json, OrderDetailResult.class);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 取司机信息
     * @param customerOrderId
     * @param driverId
     * @return
     */
    public static DriverInfoResult getDriverInfo(String customerOrderId, String driverId) {
        CloseableHttpClient httpClient = HttpClientCreater.create();
        HttpGet get = new HttpGet();
        try {
            wrapGet(get, "/v2/orders/"+customerOrderId+"/drivers/" + driverId);
            CloseableHttpResponse response = httpClient.execute(get);
            //执行请求
            int statusCode = response.getStatusLine().getStatusCode();
            String json = "";
            if (statusCode == 200 || statusCode == 201) {
                json = EntityUtils.toString(response.getEntity());
            } else {
                String message = checkError(response);
                response.close();
                throw new LalamoveException(message);
            }
            response.close();
            DriverInfoResult result = gson.fromJson(json, DriverInfoResult.class);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 查询司机的位置
     * @param customerOrderId
     * @param driverId
     * @return
     */
    public static DriverLocResult getDriverLocation(String customerOrderId, String driverId) {
        CloseableHttpClient httpClient = HttpClientCreater.create();
        HttpGet get = new HttpGet();
        try {
            wrapGet(get, "/v2/orders/"+customerOrderId+"/drivers/"+driverId+"/location");
            CloseableHttpResponse response = httpClient.execute(get);
            //执行请求
            int statusCode = response.getStatusLine().getStatusCode();
            String json = "";
            if (statusCode == 200 || statusCode == 201) {
                json = EntityUtils.toString(response.getEntity());
            } else {
                String message = checkError(response);
                response.close();
                throw new LalamoveException(message);
            }
            response.close();
            DriverLocResult result = gson.fromJson(json, DriverLocResult.class);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 取消订单
     * @param customerOrderId
     * @return
     */
    public static String cancelOrder(String customerOrderId) {
        CloseableHttpClient httpClient = HttpClientCreater.create();
        HttpPut put = new HttpPut();
        try {
            wrapPut(put, "/v2/orders/"+customerOrderId+"/cancel", "{}");
            CloseableHttpResponse response = httpClient.execute(put);
            //执行请求
            int statusCode = response.getStatusLine().getStatusCode();
            String result = "";
            if (statusCode == 200 || statusCode == 201) {
                result = "OK";
            } else {
                String message = checkError(response);
                result = message;
            }
            response.close();
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private static QuotationResult executeQuotation(Address from, List<Address> stops, Date scheduleTime) {
        //创建请求体
        JsonObject body = new JsonObject();
        body.addProperty("scheduleAt", formatUTCTime(scheduleTime==null?new Date(System.currentTimeMillis() + 10 * 60000):scheduleTime));
        body.addProperty("serviceType", Environment.serviceType);
        //非必需属性
        body.add("specialRequests", new JsonArray());
        //发货人信息
        JsonObject shipper = new JsonObject();
        shipper.addProperty("name", from.getName());
        shipper.addProperty("phone", from.getPhone());
        body.add("requesterContact", shipper);
        //stops经停站
        final JsonArray stopsArray = new JsonArray();
        //发货站点
        stopsArray.add(wrapStopInfo(from));
        //收货站点
        stops.forEach(a->stopsArray.add(wrapStopInfo(a)));
        body.add("stops", stopsArray);
        //收货人信息
        body.add("deliveries", wrapDeliveries(stops));

        String jsonBody = body.toString();
        System.out.println(jsonBody);

        //执行请求
        try {
            CloseableHttpClient httpClient = HttpClientCreater.create();
            HttpPost post = new HttpPost();
            wrapPost(post, Environment.quotationsUrl, jsonBody);
            CloseableHttpResponse response = httpClient.execute(post);
            int statusCode = response.getStatusLine().getStatusCode();
            String json = "";
            if (statusCode == 200 || statusCode == 201) {
                json = EntityUtils.toString(response.getEntity());
            } else {
                String message = checkError(response);
                response.close();
                throw new LalamoveException(message);
            }
            response.close();
            QuotationResult result = gson.fromJson(json, QuotationResult.class);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private static JsonObject wrapStopInfo(Address address) {
        JsonObject stop = new JsonObject();
        //有经纬度坐标
        if (address.getLocation() != null) {
            JsonObject location = new JsonObject();
            location.addProperty("lat", address.getLocation().getLat());
            location.addProperty("lng", address.getLocation().getLng());
            stop.add("location", location);
        }
        //发货地址
        JsonObject addresses = new JsonObject();
        JsonObject addr = new JsonObject();
        addr.addProperty("displayString", address.getDisplayString());
        addr.addProperty("country", Environment.country);
        addresses.add(Environment.placeId, addr);
        stop.add("addresses", addresses);
        return stop;
    }

    private static JsonArray wrapDeliveries(List<Address> stops) {
        JsonArray jsonArray = new JsonArray();
        for (int i=0;i<stops.size();i++) {
            Address address = stops.get(i);
            JsonObject deliveries = new JsonObject();
            deliveries.addProperty("toStop", i + 1);
            JsonObject toContact = new JsonObject();
            toContact.addProperty("name", address.getName());
            toContact.addProperty("phone", address.getPhone());
            deliveries.add("toContact", toContact);
            deliveries.addProperty("remarks", address.getRemarks());
            jsonArray.add(deliveries);
        }
        return jsonArray;
    }

    private static HttpPost wrapPost(HttpPost post, String url, String body) throws Exception {
        String URI = "https://" + Environment.hostname + url;
        //请求的url
        post.setURI(new URI(URI));
        //设置头信息
        post.setHeader("Content-Type", "application/json");
        String data = System.currentTimeMillis() + "\r\nPOST\r\n" + url + "\r\n\r\n" + body;
        post.setHeader("Authorization", "hmac "+Environment.apikey+":"+System.currentTimeMillis()+":" + genSignature(data, Environment.secret));
        post.setHeader("X-LLM-Country", Environment.country);
        StringEntity entity = new StringEntity(body, ContentType.APPLICATION_JSON);
        post.setEntity(entity);
        return post;
    }

    private static HttpPut wrapPut(HttpPut put, String url, String body) throws Exception {
        String URI = "https://" + Environment.hostname + url;
        //请求的url
        put.setURI(new URI(URI));
        //设置头信息
        put.setHeader("Content-Type", "application/json");
        String data = System.currentTimeMillis() + "\r\nPUT\r\n" + url + "\r\n\r\n" + body;
        put.setHeader("Authorization", "hmac "+Environment.apikey+":"+System.currentTimeMillis()+":" + genSignature(data, Environment.secret));
        put.setHeader("X-LLM-Country", Environment.country);
        StringEntity entity = new StringEntity(body, ContentType.APPLICATION_JSON);
        put.setEntity(entity);
        return put;
    }

    private static HttpGet wrapGet(HttpGet get, String url) throws Exception {
        String URI = "https://" + Environment.hostname + url;
        //请求的url
        get.setURI(new URI(URI));
        //设置头信息
        get.setHeader("Content-Type", "application/json");
        String data = System.currentTimeMillis() + "\r\nGET\r\n" + url + "\r\n\r\n";
        get.setHeader("Authorization", "hmac "+Environment.apikey+":"+System.currentTimeMillis()+":" + genSignature(data, Environment.secret));
        get.setHeader("X-LLM-Country", Environment.country);
        return get;
    }

    private static String genSignature(String data, String key) throws Exception {

        Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
        SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
        sha256_HMAC.init(secret_key);
        byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
        StringBuilder sb = new StringBuilder();
        for (byte item : array) {
            sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
        }

        return sb.toString();
    }

    private static String formatUTCTime(Date time) {
        SimpleDateFormat sdf = sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
        sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
        return sdf.format(time);
    }

    private static String checkError(CloseableHttpResponse response) {
        int statusCode = response.getStatusLine().getStatusCode();
        HttpEntity entity = response.getEntity();
        String message = "";
        try {
            message = EntityUtils.toString(entity);
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (statusCode == 400) {
            return "400 Invalid json body submitted!\nmessage:" + message;
        }
        if (statusCode == 401) {
            return "401 Authentication failed\nmessage:" + message;
        }
        if (statusCode == 403) {
            return "403 Forbidden\nmessage:" + message;
        }
        if (statusCode == 404) {
            return "404 Not Found\nmessage:" + message;
        }
        if (statusCode == 409) {
            return "409 Conflict\nmessage:" + message;
        }
        if (statusCode == 429) {
            return "429 Too Many Requests\nmessage:" + message;
        }
        if (statusCode == 500) {
            return "500 Internal Server Error\nmessage:" + message;
        }
        return statusCode + "Returns an unknown code\nmessage:" + message;
    }

    private static PlaceOrderResult executePlaceOrder(Address from, List<Address> stops, Date scheduleTime, int amount) {
        //创建请求体
        JsonObject body = new JsonObject();
        body.addProperty("scheduleAt", formatUTCTime(scheduleTime==null?new Date(System.currentTimeMillis() + 10 * 60000):scheduleTime));
        body.addProperty("serviceType", Environment.serviceType);
        //非必需属性
        body.add("specialRequests", new JsonArray());
        //发货人信息
        JsonObject shipper = new JsonObject();
        shipper.addProperty("name", from.getName());
        shipper.addProperty("phone", from.getPhone());
        body.add("requesterContact", shipper);
        //stops经停站
        final JsonArray stopsArray = new JsonArray();
        //发货站点
        stopsArray.add(wrapStopInfo(from));
        //收货站点
        stops.forEach(a->stopsArray.add(wrapStopInfo(a)));
        body.add("stops", stopsArray);
        //收货人信息
        body.add("deliveries", wrapDeliveries(stops));
        //邮费信息
        JsonObject quotedTotalFee = new JsonObject();
        quotedTotalFee.addProperty("amount", amount + "");
        quotedTotalFee.addProperty("currency", Environment.currency);
        body.add("quotedTotalFee", quotedTotalFee);

        String jsonBody = body.toString();
        System.out.println(jsonBody);

        //执行请求
        try {
            CloseableHttpClient httpClient = HttpClientCreater.create();
            HttpPost post = new HttpPost();
            wrapPost(post, Environment.ordersUrl, jsonBody);
            CloseableHttpResponse response = httpClient.execute(post);
            int statusCode = response.getStatusLine().getStatusCode();
            String json = "";
            if (statusCode == 200 || statusCode == 201) {
                json = EntityUtils.toString(response.getEntity());
            } else {
                String message = checkError(response);
                response.close();
                throw new LalamoveException(message);
            }
            response.close();
            PlaceOrderResult result = gson.fromJson(json, PlaceOrderResult.class);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

}
